package org.kwin.spring.web.handler;

import org.kwin.common.util.JsonUtil;
import org.kwin.spring.util.FilePath;
import org.kwin.spring.web.annotation.RequestMapping;
import org.kwin.spring.web.annotation.RequestParam;
import org.kwin.spring.web.annotation.ResponseBody;
import org.kwin.spring.web.enums.RequestMethod;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author : wangkai
 * @version V1.0
 * @Package org.kwin.spring.web.handler
 * @Description:
 * @date 2021年09月28日 16:45
 **/
public class HandlerMapping {
    
    private static Map<String, HandlerMethod> handlerMap = new ConcurrentHashMap();
    
    public static void registerMapping(Class clzz, Object bean) {
        String url = "";
        if(clzz.isAnnotationPresent(RequestMapping.class)) {
            RequestMapping requestMapping = (RequestMapping) clzz.getAnnotation(RequestMapping.class);
            url = preUrl(requestMapping.value());
        }
        Method[] methods = clzz.getDeclaredMethods();
    
        HandlerMethod handlerMethod = null;
        for(Method method : methods) {
            if(method.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
                String childUrl = preUrl(requestMapping.value());
                handlerMethod = new HandlerMethod(bean, method);
                handlerMap.put(url + childUrl, handlerMethod);
            }
        }
    }
    
    private static String preUrl(String url) {
        if(url == null || url.length() <= 0) {
            return url;
        }
        if(url.charAt(0) != '/') {
            url = '/' + url;
        }
        if(url.endsWith("/")) {
            url = url.substring(0, url.length() - 1);
        }
        return url;
    }
    
    public void request(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        String url = req.getRequestURI();
        if(!handlerMap.containsKey(url)) {
            resp.setContentType("text/html; charset=utf-8");
            resp.getWriter().println("<h1>" + url + "请求不存在，404</h1>");
            return;
        }
        
        HandlerMethod handlerMethod = handlerMap.get(url);
        
        if(!validMethodType(req, resp, handlerMethod.method)) {
//            resp.getWriter().println("，请求错误，405</h1>");
            return;
        }
        // 入参
        Object[] args = initParameter(req, resp, handlerMethod.method);
    
        Object result = null;
        try {
            result = handlerMethod.method.invoke(handlerMethod.bean, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            resp.setContentType("text/html; charset=utf-8");
            resp.getWriter().println("<h1>" + url + "请求出错" + e.getMessage() + "</h1>");
            return;
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            resp.setContentType("text/html; charset=utf-8");
            resp.getWriter().println("<h1>" + url + "请求出错" + e.getMessage() + "</h1>");
            return;
        }
    
        if (handlerMethod.method.getAnnotation(ResponseBody.class) != null) {
            // 返回json请求
            resp.setContentType("text/json; charset=utf-8");
            resp.getWriter().println(JsonUtil.ObjToJsonStr(result));
            return;
        }
        if (result instanceof String)
        {
            // 是否重定向
            if (result.toString().startsWith("redirect:"))
            {
                url = result.toString();
                System.out.println("重定向url=" + url);
                url = "/" + url.substring(9);
                if (handlerMap.containsKey(url))
                {
                    resp.sendRedirect(".." + url);
                    // no return error
                    return;
                } else
                {
                    resp.getWriter().println("<h1>" + url + " not find，404</h1>");
                    return;
                }
            }
            String uuidKey = UUID.randomUUID().toString();
            String uuidValue = UUID.randomUUID().toString();
            req.setAttribute(uuidKey, uuidValue);
            File file = new File(FilePath.RESOURCES_PATH + "key.properties");
            FileOutputStream fos = new FileOutputStream(file);
            fos.write((uuidKey + "=" + uuidValue).getBytes());
            fos.close();
            req.getRequestDispatcher(result.toString() + ".red").forward(req, resp);
        }
        
        if (result instanceof ModelAndView)
        {
            ModelAndView modelAndView = (ModelAndView) result;
            for (String key : modelAndView.keySet())
            {
                req.setAttribute(key, modelAndView.get(key));
            }
            String uuidKey = UUID.randomUUID().toString();
            String uuidValue = UUID.randomUUID().toString();
            PrintWriter pw = new PrintWriter(new File(FilePath.RESOURCES_PATH + "key.properties"));
            pw.write(uuidKey + "=" + uuidValue);
            Cookie cookie = new Cookie(uuidKey, uuidValue);
            resp.addCookie(cookie);
            req.getRequestDispatcher(modelAndView.getViewName() + ".red").forward(req, resp);
        } else
        {
            resp.getWriter().println("<h1>返回值异常，500</h1>");
        }
    
    }
    
    private boolean validMethodType(HttpServletRequest req, HttpServletResponse resp, Method method)
        throws IOException {
        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
        if(requestMapping.method().length == 0) {
            return true;
        }
    
        String methodType = req.getMethod();
        for(RequestMethod requestMethod : requestMapping.method()) {
            if (requestMethod.toString().equals(methodType)) {
                return true;
            }
        }
        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().println("<h1>你的请求类型为：" + methodType + "，但是接受类型是：" + Arrays.toString(requestMapping.method()) + "，请求错误，405</h1>");
        return false;
    }
    
    private Object[] initParameter(HttpServletRequest req, HttpServletResponse resp, Method method)
    {
        Parameter[] parameters = method.getParameters();
        Map<String, String[]> parameMap = req.getParameterMap();
        
        Object[] args = new Object[parameters.length];
        // 参数解析
        for (int i = 0; i < args.length; i++)
        {
            if (parameters[i].getType() == HttpServletRequest.class)
            {
                args[i] = req;
            } else if (parameters[i].getType() == HttpServletResponse.class)
            {
                args[i] = resp;
            } else if (parameters[i].getType() == ModelAndView.class)
            {
                args[i] = new ModelAndView();
            } else if (parameters[i].isAnnotationPresent(RequestParam.class))
            {
                RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
                String key = requestParam.value();
                if (parameMap.containsKey(key))
                {
                    if (parameMap.get(key).length == 1)
                    {
                        args[i] = setArgs(parameters[i],parameMap.get(key)[0]);
                    } else
                    {
                        args[i] = parameMap.get(key);
                    }
                }
            } else {
                try
                {
                    Object obj = parameters[i].getType().getConstructor().newInstance();
                    Field[] fields = parameters[i].getType().getDeclaredFields();
                    for (Field f : fields)
                    {
                        if (parameMap.containsKey(f.getName()))
                        {
                            f.setAccessible(true);
                            if (parameMap.get(f.getName()) != null)
                            {
                                setValue(f, obj, parameMap.get(f.getName())[0]);
                            }
                        }
                    }
                    args[i] = obj;
                } catch (Exception e)
                {
                    System.err.println("无法创建对象：" + parameters[i].getType());
                    e.printStackTrace();
                }
                System.out.println("无法初始化的类型：" + parameters[i].getType());
            }
        }
        
        return args;
    }
    
    private Object setArgs(Parameter parameter, String s)
    {
        if(parameter.getType()==Long.class||parameter.getType()==long.class)
        {
            return Long.parseLong(s);
        }
        if(parameter.getType()==Integer.class||parameter.getType()==int.class)
        {
            return Integer.parseInt(s);
        }
        if(parameter.getType()==Short.class||parameter.getType()==short.class)
        {
            return Short.parseShort(s);
        }
        if(parameter.getType()==Byte.class||parameter.getType()==byte.class)
        {
            return Byte.parseByte(s);
        }
        if(parameter.getType()==Float.class||parameter.getType()==float.class)
        {
            return Float.parseFloat(s);
        }
        if(parameter.getType()==Double.class||parameter.getType()==double.class)
        {
            return Double.parseDouble(s);
        }
        if(parameter.getType()==Boolean.class||parameter.getType()==boolean.class)
        {
            return Boolean.parseBoolean(s);
        }
        return s;
    }
    
    private void setValue(Field f, Object obj, String s) throws Exception
    {
        if (f.getType() == String.class)
        {
            f.set(obj, s);
        } else if (f.getType() == Integer.class || f.getType() == int.class)
        {
            f.set(obj, Integer.parseInt(s));
        } else if (f.getType() == Double.class || f.getType() == double.class)
        {
            f.set(obj, Double.parseDouble(s));
        } else if (f.getType() == Float.class || f.getType() == float.class)
        {
            f.set(obj, Float.parseFloat(s));
        } else if (f.getType() == Long.class || f.getType() == long.class)
        {
            System.out.println("long类型");
            f.set(obj, Long.parseLong(s));
        } else if (f.getType() == Byte.class || f.getType() == byte.class)
        {
            f.set(obj, Byte.parseByte(s));
        } else if (f.getType() == Short.class || f.getType() == short.class)
        {
            f.set(obj, Short.parseShort(s));
        } else if (f.getType() == Character.class || f.getType() == char.class)
        {
            f.set(obj, s);
        }
    }
}
